前言
快一个月没有写自己的博客了,由于最近换了工作,换了居住地,所以有一些杂事需要处理,从今天开始恢复正常,不赘述了。进入今天的主题 —– 简易的手电筒。
这个Demo中使用的是比较新的API,M版本之后添加的针对于手电筒的接口。这里使用的是Camera的API2接口,主要使用CameraManager中针对于闪光灯的一些方法,对于Camera API2的接口,后面在涉及相机应用的时候,API1和API2应该都会梳理一下,到时候再仔细的研究一下。
思路
实现一个简单的手电筒,考虑到M版本上新增的接口,可以直接通过setTorchMode来改变闪光灯的状态,实现开关,然后根据当前的闪光灯状态,有回调函数,若其他的应用打开了闪光灯或者是关闭了闪光灯,该应用要作出对应的调整,同时,开启和关闭的过程,需要有明显的用户感知和提示,这就要结合NotificationManager和CameraManager的接口一起实现了。
接口介绍
CameraManager.java(frameworks/base/core/java/android/hardware/camera2)
方法 |
含义 |
TorchCallback |
针对闪光灯的回调 |
AvailabilityCallback |
针对相机是否可用的回调 |
CameraManager() |
构造函数 |
getCameraIdList() |
获取相机的Id |
registerAvailabilityCallback() |
注册相机是否可用的回调 |
unregisterAvailabilityCallback() |
解除注册 |
registerTorchCallback() |
注册针对闪光灯状态的回调 |
unregisterTorchCallback() |
解除注册 |
getCameraCharacteristics() |
传入参数为相机的id,获取相的一参数信息,如支持的预览大小,支持的滤镜等等 |
openCamera() |
传入的参数为相机的id和状态回StateCallback,这个是在CameraDevice中定义的,打开相机操作 |
setTorchMode() |
设置闪光灯的状态 |
实战代码
1.布局文件
由于是手电筒,布局文件很简单,主布局中只有一个button
activity_custom_button.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/flash_content" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black" tools:context=".FlashActivity">
<Button android:id="@+id/bt_flash" android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="center" android:background="@drawable/flash_open" />
</FrameLayout>
|
显示当前闪光灯被占用的自定义Toast布局
busy_toast.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TextView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/busy_snackbar_bg" android:gravity="center" android:padding="@dimen/activity_horizontal_margin" android:text="FlashLight is Busy , Sorry!" android:textStyle="bold" />
</LinearLayout>
|
2.代码文件
主要就是两个类,一个是主Activity,一个就是用来执行notification的pendingintent的广播接收器
FlashActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
| package mraz.com.custombutton;
import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraManager; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.app.NotificationCompat; import android.view.Gravity; import android.view.View; import android.widget.Button; import android.widget.FrameLayout; import android.widget.Toast;
@TargetApi(Build.VERSION_CODES.M) public class FlashActivity extends AppCompatActivity {
public static final String CLOSE_FLASH_ACTION = "android.intent.action.close_flash"; private static final int NOTIFICATIONID = 0; private Button btFlash; private boolean mIsFlashOn = false; private CameraManager cameraManager = null; private String[] mCameraIds; private Notification mFlashOnNotification = null; private NotificationManager notificationManager = null; private boolean isFlashAvailbale = true; private FrameLayout mContentPanel = null;
private CameraManager.TorchCallback torchCallback = new CameraManager.TorchCallback() { @Override public void onTorchModeUnavailable(String cameraId) { super.onTorchModeUnavailable(cameraId); if (cameraId.equals(mCameraIds[0]) && mIsFlashOn) { reverseFlashState(); } isFlashAvailbale = false; System.out.println("cameraId = " + cameraId + " onTorchModeUnavailable"); }
@Override public void onTorchModeChanged(String cameraId, boolean enabled) { super.onTorchModeChanged(cameraId, enabled); isFlashAvailbale = true; System.out.println("cameraid = " + cameraId + " enabled = " + enabled + " misFlashOn = " + mIsFlashOn); if (cameraId.equals(mCameraIds[0]) && enabled == false && mIsFlashOn) { reverseFlashState(); } System.out.println("cameraId = " + cameraId + " onTorchModeChanged enabled = " + enabled); } };
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_button);
btFlash = (Button) findViewById(R.id.bt_flash); mContentPanel = (FrameLayout) findViewById(R.id.flash_content);
btFlash.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { reverseFlashState(); } }); changeFlashUi(mIsFlashOn); }
private void reverseFlashState() { if (!isFlashAvailbale) { showFlashBusy(); return; } changeFlashState(mIsFlashOn); mIsFlashOn = !mIsFlashOn; changeFlashUi(mIsFlashOn); applyNotification(mIsFlashOn); }
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void applyNotification(boolean isFlashOn) { if (!isFlashOn) { dismissNotification(); return; } if (mFlashOnNotification != null && notificationManager != null) { notificationManager.notify(NOTIFICATIONID, mFlashOnNotification); } }
@Override protected void onResume() { super.onResume(); cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE); notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); cameraManager.registerTorchCallback(torchCallback, null); getCameraList(); generateNotify(); }
@Override protected void onDestroy() { super.onDestroy(); cameraManager.unregisterTorchCallback(torchCallback); }
@TargetApi(Build.VERSION_CODES.M) private void changeFlashState(boolean isFlashOn) { if (cameraManager != null && mCameraIds != null) { try { cameraManager.setTorchMode(mCameraIds[0], !isFlashOn); } catch (CameraAccessException e) { e.printStackTrace(); } } }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) private void getCameraList() { if (cameraManager != null) { try { mCameraIds = cameraManager.getCameraIdList(); } catch (CameraAccessException e) { e.printStackTrace(); } } }
private void changeFlashUi(boolean isFlashOn) { if (isFlashOn) { btFlash.setBackgroundResource(R.drawable.flash_open); } else { btFlash.setBackgroundResource(R.drawable.flash_close); } }
private Bitmap createNotificationLargeIcon(Context c) { Resources res = c.getResources(); int width = (int) res.getDimension(android.R.dimen.notification_large_icon_width); int height = (int) res.getDimension(android.R.dimen.notification_large_icon_height); Bitmap result = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(res, R.mipmap.ic_flash_on_normal), width, height, false); return result; }
private void generateNotify() { if (mFlashOnNotification != null) return; NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setLargeIcon(createNotificationLargeIcon(this)) .setContentTitle("手电筒已开启") .setContentText("点击可关闭手电筒") .setSmallIcon(R.mipmap.ic_flash_off_normal, 3) .setContentIntent(createCloseFlashPendingIntent()); mFlashOnNotification = builder.build(); }
private void dismissNotification() { if (notificationManager != null && mFlashOnNotification != null) { notificationManager.cancel(NOTIFICATIONID); } }
private PendingIntent createCloseFlashPendingIntent() { Intent intent = new Intent(); intent.setClass(this, FlashCloseReceiver.class); intent.setAction(CLOSE_FLASH_ACTION);
return PendingIntent.getBroadcast(this, 0, intent, 0); }
private void showFlashBusy() { View toastContent = getLayoutInflater().inflate(R.layout.busy_toast, null, false); Toast toast = new Toast(this); toast.setView(toastContent); toast.setGravity(Gravity.CENTER, 0, 0); toast.setDuration(Toast.LENGTH_LONG); toast.show(); } }
|
FlashCloseReceiver.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package mraz.com.custombutton;
import android.annotation.TargetApi; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraManager; import android.os.Build;
public class FlashCloseReceiver extends BroadcastReceiver { CameraManager mCameraManager = null; String[] mCameraIds = null;
public FlashCloseReceiver() { }
@TargetApi(Build.VERSION_CODES.M) @Override public void onReceive(Context context, Intent intent) { System.out.println("onReceiver"); mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); if (mCameraManager != null) { try { mCameraIds = mCameraManager.getCameraIdList(); } catch (CameraAccessException e) { e.printStackTrace(); } } String action = intent.getAction(); if (action.equals(FlashActivity.CLOSE_FLASH_ACTION)) { if (mCameraManager != null && mCameraIds != null && mCameraIds.length != 0) { try { System.out.println("setTorchMode"); mCameraManager.setTorchMode(mCameraIds[0], false); } catch (CameraAccessException e) { e.printStackTrace(); } } } } }
|
实际效果图
手电筒关闭状态
手电筒开启状态
手电筒开启状态提示信息
备注
由于开发时间比较短,测试可能不充分,有问题欢迎留言讨论~